#include <math.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


#if defined(___SYS_I486)
#if !defined(___SYS_I386)
#define ___SYS_I386
#endif
#endif


typedef void xx;
typedef float R4;
typedef unsigned char  N1;
typedef unsigned short int N2;
typedef unsigned long  int N4;
typedef   signed char  I1;
typedef   signed short int I2;
typedef   signed long  int I4;
typedef xx *Ptr;
typedef N1 *Str;


#if !defined(NULL)
#define NULL                      ((Ptr)0ul)
#endif
#define NO                        0
#define YES                       1
#define BEG                       do {
#define END                       } while (0)
#define DEB0(s)                   msg(4,s)
#define DEB1(s,n1)                msg(4,s,n1)
#define DEB2(s,n1,n2)             msg(4,s,n1,n2)
#define INF0(s)                   msg(3,s)
#define INF1(s,n1)                msg(3,s,n1)
#define INF2(s,n1,n2)             msg(3,s,n1,n2)
#define MSG1(s,n1)                msg(3,"[%08lX] " s "\n",n1)
#define MSG2(s,n1,n2)             msg(3,"[%08lX] " s "\n",n1,n2)
#define MSG3(s,n1,n2,n3)          msg(3,"[%08lX] " s "\n",n1,n2,n3)
#define MSG4(s,n1,n2,n3,n4)       msg(3,"[%08lX] " s "\n",n1,n2,n3,n4)
#define WARN1(s,n1)               msg(2,"[%08lX] \n  " s "\n",n1)
#define WARN2(s,n1,n2)            msg(2,"[%08lX] \n  " s "\n",n1,n2)
#define ERR1(s,n1)                BEG msg(1,"[%08lX] " s "  {%lu}\n",n1,n1); \
                                  return (-1); END
#define ERR2(s,n1,n2)             BEG msg(1,"[%08lX] " s "  {%lu}\n",n1,n2,n1); \
                                  return (-1); END
#define ERR3(s,n1,n2,n3)          BEG msg(1,"[%08lX] " s "  {%lu}\n",n1,n2,n3,n1); \
                                  return (-1); END
#define GETC                      BEG ii=fgetc(prg.fi); cc=(N1)ii; \
                                  if (ii!=-1) ++nn; END
#define ddcp(dd1,dd2)             MemCpy(dd1,dd2,64)
#define ddyx(dd,xd,y,x)           ((dd)+(((y)*(xd)+(x))<<6))

#define CNUM                      3          // CompNum
#define QMAX                      4          // QuantMax
#define HNUM                      2
#define HMAX                      (2*HNUM)   // HufMax


typedef struct _tProg
{ N1 msg;
  FILE *fi,*fo,*fb;
} tProg;
typedef struct _tApp
{ // 1.1
  N1 ver[2];
  // 0:pix,1:dpi,2:dpc
  N1 units;
  N2 xden,yden;
  N1 xthumb,ythumb;
} tApp;
typedef struct _tSof
{ // 8 bits
  N1 prec;
  N2 xl,yl;
  N1 comps;
} tSof;
typedef struct _tComp
{ N1 cid;
  // 1-4
  N1 xd,yd;
  // 0-3
  N1 qid;
} tComp;
typedef struct _tSos
{ N1 comps;
  N1 q1,q2;
  N1 ah,al;
  N2 dri;
} tSos;
typedef struct _tComp2
{ N1 cid;
  // 0-1
  N1 hdc;
  N1 hac;
} tComp2;
typedef struct _tQtab
{ // 0:8 bits
  N1 prec;
  N1 qid;
  N2 tab[64];
} tQtab;
typedef struct _tHtab
{ // 0:DC,1:AC
  N1 typ;
  N1 hid;
  N1 len[16];
  N1 *val;

  N2 hufnum;
  N1 *hufsiz;
  N2 *hufcod;
  N2 hufmin[16];
  N2 hufmax[16];
  N2 hufidx[16];
} tHtab;
typedef struct _tBmp
{ FILE *f1;
  N4 xm,ym,xm2;
  N4 xl,yl,xl2;
  N1 *pp;
} tBmp;

static tProg prg;
static Str units[]=
{ "pix", "dpi", "dpc"
};
static Str htyp[]=
{ "dc", "ac"
};


xx msg(N1 msg,Str fmt,...)
{ Ptr pp=(Ptr)(&fmt+1);
  if (msg>prg.msg) return;
  vfprintf(prg.fo,fmt,pp);
  fflush(prg.fo);
}
xx dumpN1(Str s1,N1 *dd)
{ N2 i1; for (i1=0;i1<64;++i1)
  { if (!i1)
      DEB1("\n[........] (%s):\n",s1);
    if ((i1&7)==0)
      DEB0("[........] ");
    DEB1("  %u",(N2)*(dd++));
    if ((i1&7)==7)
      DEB0("\n");
  }
  DEB0("[........] ");
}
xx dumpI2(Str s1,I2 *dd)
{ N2 i1; for (i1=0;i1<64;++i1)
  { if (!i1)
      DEB1("\n[........] (%s):\n",s1);
    if ((i1&7)==0)
      DEB0("[........] ");
    DEB1("  %i",*(dd++));
    if ((i1&7)==7)
      DEB0("\n");
  }
  DEB0("[........] ");
}
xx dumpI4(Str s1,I4 *dd)
{ N2 i1; for (i1=0;i1<64;++i1)
  { if (!i1)
      DEB1("\n[........] (%s):\n",s1);
    if ((i1&7)==0)
      DEB0("[........] ");
    DEB1("  %li",*(dd++));
    if ((i1&7)==7)
      DEB0("\n");
  }
  DEB0("[........] ");
}
xx dumpR4(Str s1,R4 *dd)
{ N2 i1; for (i1=0;i1<64;++i1)
  { if (!i1)
      DEB1("\n[........] (%s):\n",s1);
    if ((i1&7)==0)
      DEB0("[........] ");
    DEB1("  %f",*(dd++));
    if ((i1&7)==7)
      DEB0("\n");
  }
  DEB0("[........] ");
}
Ptr MemGet(N4 len)
{ if (!len) return (NULL);
  return (malloc(len));
}
xx MemRel(Ptr p)
{ if (p) free(p);
}
I4 MemCpy(Str s1,Str s2,N4 l)
{ if (l && (!s1 || !s2))
    return (-1l);
  if (!l || s1==s2) return (0);
  if (s1>s2)
  { while (l--) *(s2++)=*(s1++);
  } else
  { s1+=l-1; s2+=l-1;
    while (l--) *(s2--)=*(s1--);
  }
  return (0);
}
Str StrCmp(Str s1,Str s2)
{ if (!s1 || !s2)
  { if (s2) s1=s2;
    if (s1 && *s1)
      return (s1);
    return (NULL);
  }
  for (;*s1 && *s2;++s1,++s2)
    if (*s1!=*s2) return (s1);
  if (*s1!=*s2) return (s1);
  return (NULL);
}
R4 RoundR4(R4 r1)
{ return ((R4)floor(r1+0.5));
}
tQtab *getqt(tQtab *qtab,N1 qid)
{ N1 n1; for (n1=0;n1<QMAX;++n1,++qtab)
  { if (qtab->qid==0xFF)
      continue;
    if (qtab->qid==qid)
      return (qtab);
  }
  return (NULL);
}
tHtab *getht(tHtab *htab,N1 hid,N1 typ)
{ N1 n1; for (n1=0;n1<HMAX;++n1,++htab)
  { if (htab->hid==0xFF)
      continue;
    if (htab->hid==hid &&
        htab->typ==typ)
      return (htab);
  }
  return (NULL);
}


typedef struct _tNextBit
{ N4 *nn;
  FILE *f1;
  N1 cnt,bb;
} tNextBit;
I4 conv1(N4 *nn,tSof *sof,tComp *comp,tComp2 *comp2,
  tHtab *htab,tQtab *qtab,tNextBit *bb,FILE *f1);
I4 NextBit(tNextBit *r1);
I4 NextVal(tNextBit *r1,N1 bits);
I4 Decode(tNextBit *r1,tHtab *htab);
I4 GetDC(tNextBit *r1,tHtab *htab,I2 *dc);
I4 GetAC(tNextBit *r1,tHtab *htab,I2 *ac);
I4 ZigZag(N1 *zz,R4 *dd1,R4 *dd2);
I4 LevelShift1(N1 *dd1,R4 *dd2);
I4 LevelShift2(R4 *dd1,N1 *dd2);
I4 Quant1(tQtab *qtab,R4 *dd1,I2 *dd2);
I4 Quant2(tQtab *qtab,I2 *dd1,R4 *dd2);
I4 DCT1(R4 *tt,R4 *dd1,R4 *dd2);
I4 DCT2(R4 *tt,R4 *dd1,R4 *dd2);
R4 DCT1x(R4 *tt,R4 *dd,N1 u,N1 v);
R4 DCT2x(R4 *tt,R4 *dd,N1 x,N1 y);
xx Color1(N1 *cr,N1 *cg,N1 *cb);
xx Color2(N1 *c1,N1 *c2,N1 *c3);
I4 BmpBeg(tBmp *bmp,FILE *f1,
  N4 xm,N4 ym,N4 xl,N4 yl);
I4 BmpEnd(tBmp *bmp);
I4 BmpSet(tBmp *bmp,N4 x,N4 y,
  N1 cr,N1 cg,N1 cb);

I4 opts(int argc,char **argv)
{ FILE *f1; N4 i;
  prg.msg=4;
  prg.fi=stdin;
  prg.fo=stdout;
  prg.fb=NULL;

  for (++argv,i=1;i<argc;++i)
  { Str s1=*(argv++);
    if (s1[0]!='-') continue;
    switch (s1[1])
    { case 'h':
        puts("Args: -h| [-v(0-4)] [-i<file_jpg>] [-o<file_out>] [-b<file_bmp>]");
        puts("  -v:4, -i:<stdin>, -o:<stdout>, -b:<none>");
        exit(0);
      case 'v':
        prg.msg=s1[2]-'0';
        break;
      case 'i':
        if (!(f1=fopen(s1+2,"rb")))
          ERR1("Error: Open Input",0l);
        prg.fi=f1;
        break;
      case 'o':
        if (!(f1=fopen(s1+2,"wb")))
          ERR1("Error: Open Output",0l);
        prg.fo=f1;
        break;
      case 'b':
        if (!(f1=fopen(s1+2,"wb")))
          ERR1("Error: Open Bitmap",0l);
        prg.fb=f1;
        break;
    }
  }
  return (0);
}
int main(int argc,char **argv)
{ N1 cc,c1; int ii; N4 nn=0,ll=0;
  tApp app; tSof sof; tSos sos;
  tComp comp[CNUM];
  tComp2 comp2[CNUM];
  tQtab qtab[QMAX]; N1 qn=0;
  tHtab htab[HMAX]; N1 hn=0;
  N4 n1,n2; tNextBit bb;
  N1 image=0;
  if (opts(argc,argv)==-1l)
    return (-1);
  bb.nn=&nn; bb.f1=prg.fi;
  bb.cnt=0; bb.bb=0;

#if defined(___OS_WIN)
  setmode(fileno(stdin),O_BINARY);
  setmode(fileno(stdout),O_BINARY);
  setmode(fileno(stderr),O_BINARY);
#endif

  app.ver[0]=0; app.ver[1]=0;
  sof.comps=0; sos.comps=0;
  for (c1=0;c1<QMAX;++c1)
    qtab[c1].qid=0xFF;
  for (c1=0;c1<HMAX;++c1)
    htab[c1].hid=0xFF;
  while (1)
  { GETC; if (ii==-1) break;
    if (ll) { --ll; continue; }
    if (cc!=0xFF)
    { if (image)
      { WARN1("Warn: Image: More",nn-1);
        break;
      }
      if (!app.ver[0] && !app.ver[1])
        WARN1("Warn: Image: Check",nn-1);
      if (!sof.comps || !sos.comps)
        ERR1("Error: Image: Check",nn-1);
      image=1; bb.cnt=8; bb.bb=cc;
      if (conv1(&nn,&sof,comp,comp2,
        htab,qtab,&bb,prg.fb)==-1l)
        return (-1l);
      continue;
    }
    if (cc!=0xFF) //continue;
      ERR1("Error: Missing 0xFF",nn-1);
    while (1)
    { GETC; if (ii==-1)
        ERR1("Error: Missing Marker",nn-1);
      if (cc!=0xFF) break;
    }
    switch (cc)
    { case 0x00: // Escaped 0xFF
        ERR1("Error: Unexpected 0xFF",nn-2);
        break;
      case 0xD8: // SOI
        MSG1("Marker: SOI",nn-2);
        break;
      case 0xD9: // EOI
        MSG1("Marker: EOI",nn-2);
        break;
      case 0x01: // TEM
        MSG1("Marker: TEM",nn-2);
        break;
      case 0xD0:
      case 0xD1:
      case 0xD2:
      case 0xD3:
      case 0xD4:
      case 0xD5:
      case 0xD6:
      case 0xD7: // RSTi
        WARN2("Warn: Marker: RST%u",nn-2,(N2)cc-0xD0);
        break;
      default: c1=cc;
        MSG2("Marker: 0x%02X",nn-2,(N2)cc);
        GETC; if (ii==-1)
          ERR1("Error: Missing Length/1",nn);
        ll=(N4)cc;
        GETC; if (ii==-1)
          ERR1("Error: Missing Length/2",nn-1);
        ll=(ll<<8)|(N4)cc; if (ll<2)
          ERR1("Error: Invalid length",nn-2);
        MSG3("Len: 0x%04lX (%lu)",nn-2,ll,ll);
        ll-=2; switch (c1)
        { case 0xE0:
            MSG1("Marker: APP0",nn-4);
            if (ll<14)
              ERR1("Error: Invalid length",nn-2);
            // Identifier (5)
            { N1 buf[5],*s1="JFIF";
              for (c1=0;c1<5;++c1)
              { GETC; if (ii==-1)
                  ERR1("Error: Missing ID",nn);
                buf[c1]=cc;
              }
              if (StrCmp(buf,s1))
                ERR1("Error: Invalid ID",nn-5);
              MSG2("  APP0: %s",nn-5,s1);
            } ll-=5;
            // Version (2)
            GETC; if (ii==-1)
              ERR1("Error: Missing Version/1",nn);
            app.ver[0]=cc;
            GETC; if (ii==-1)
              ERR1("Error: Missing Version/2",nn-1);
            app.ver[1]=cc;
            MSG3("  Ver: %u.%u",nn-2,
              (N2)app.ver[0],(N2)app.ver[1]);
            if (app.ver[0]!=1)
              ERR1("Error: Invalid Version/1",nn-2);
            if (app.ver[1]<1)
              WARN1("Warn: Invalid Version/2",nn-1);
            ll-=2;
            // Units (1)
            GETC; if (ii==-1)
              ERR1("Error: Missing Units",nn);
            app.units=cc;
            if (app.units>2)
              ERR2("Error: Invalid Units: %u",nn-1,(N2)app.units);
            MSG3("  Units: %s (%u)",nn-1,
              units[app.units],(N2)app.units);
            ll-=1;
            // X Density (2)
            GETC; if (ii==-1)
              ERR1("Error: Missing X Density/1",nn);
            app.xden=(N2)cc;
            GETC; if (ii==-1)
              ERR1("Error: Missing X Density/2",nn-1);
            app.xden=(app.xden<<8)|(N2)cc;
            MSG2("  XDen: %u",nn-2,app.xden);
            ll-=2;
            // Y Density (2)
            GETC; if (ii==-1)
              ERR1("Error: Missing Y Density/1",nn);
            app.yden=(N2)cc;
            GETC; if (ii==-1)
              ERR1("Error: Missing Y Density/2",nn-1);
            app.yden=(app.yden<<8)|(N2)cc;
            MSG2("  YDen: %u",nn-2,app.yden);
            ll-=2;
            // X Thumbnail (1)
            GETC; if (ii==-1)
              ERR1("Error: Missing X Thumbnail",nn);
            app.xthumb=cc;
            MSG2("  XThumb: %u",nn-1,(N2)app.xthumb);
            ll-=1;
            // Y Thumbnail (1)
            GETC; if (ii==-1)
              ERR1("Error: Missing Y Thumbnail",nn);
            app.ythumb=cc;
            MSG2("  YThumb: %u",nn-1,(N2)app.ythumb);
            ll-=1;
            // Thumbnail
            if (ll!=3ul*(N4)app.xthumb*(N4)app.ythumb)
              ERR2("Error: Invalid Thumbnail[%lu]",nn,ll);
            MSG2("  Thumb[%lu]",nn,ll);
            break;
          case 0xE1:
            MSG1("Marker: APP1",nn-4);
            if (ll<5)
              ERR1("Error: Invalid length",nn-2);
            // Identifier (5)
            { N1 buf[5],*s1="Exif";
              for (c1=0;c1<5;++c1)
              { GETC; if (ii==-1)
                  ERR1("Error: Missing ID",nn);
                buf[c1]=cc;
              }
              if (StrCmp(buf,s1))
                WARN1("Warn: Invalid ID",nn-5);
              else MSG2("  APP1: %s",nn-5,s1);
            } ll-=5;
            break;
          case 0xE2:
          case 0xE3:
          case 0xE4:
          case 0xE5:
          case 0xE6:
          case 0xE7:
          case 0xE8:
          case 0xE9:
          case 0xEA:
          case 0xEB:
          case 0xEC:
          case 0xED:
          case 0xEE:
          case 0xEF:
            WARN2("Warn: Marker: APP%u",nn-4,(N2)c1-0xE0);
            break;
          case 0xC0:
          case 0xC1:
          case 0xC2:
          case 0xC3:
          case 0xC5:
          case 0xC6:
          case 0xC7:
          case 0xC9:
          case 0xCA:
          case 0xCB:
          case 0xCD:
          case 0xCE:
          case 0xCF:
            MSG2("Marker: SOF%u",nn-4,(N2)c1-0xC0);
            if (c1!=0xC0 && c1!=0xC1)
              ERR2("Error: Unknown SOF%u",nn-4,(N2)c1-0xC0);
            if (ll<6)
              ERR1("Error: Invalid length",nn-2);
            // Precision (1)
            GETC; if (ii==-1)
              ERR1("Error: Missing Precision",nn);
            sof.prec=cc;
            MSG2("  Prec: %u",nn-1,(N2)sof.prec);
            if (sof.prec!=8)
              ERR1("Error: Invalid Precision",nn-1);
            ll-=1;
            // Y Length (2)
            GETC; if (ii==-1)
              ERR1("Error: Missing Y Length/1",nn);
            sof.yl=(N2)cc;
            GETC; if (ii==-1)
              ERR1("Error: Missing Y Length/2",nn-1);
            sof.yl=(sof.yl<<8)|(N2)cc;
            MSG2("  YLen : %u",nn-2,sof.yl);
            ll-=2;
            // X Length (2)
            GETC; if (ii==-1)
              ERR1("Error: Missing X Length/1",nn);
            sof.xl=(N2)cc;
            GETC; if (ii==-1)
              ERR1("Error: Missing X Length/2",nn-1);
            sof.xl=(sof.xl<<8)|(N2)cc;
            MSG2("  XLen : %u",nn-2,sof.xl);
            ll-=2;
            // Comp Num (1)
            GETC; if (ii==-1)
              ERR1("Error: Missing Comp Num",nn);
            sof.comps=cc;
            MSG2("  CompNum: %u",nn-1,(N2)sof.comps);
            if (sof.comps!=CNUM)
              ERR1("Error: Invalid Comp Num",nn-1);
            ll-=1;
            // Components [C/8,X/4,Y/4,Q/8]
            if (ll!=3*(N2)sof.comps)
              ERR2("Error: Invalid length: %lu",nn,ll);
            for (c1=0;c1<sof.comps;++c1)
            { // CID/8
              GETC; if (ii==-1)
                ERR1("Error: Missing CID",nn);
              comp[c1].cid=cc;
              MSG2("  CID: %u",nn-1,(N2)comp[c1].cid);
              // Xd/4,Yd/4
              GETC; if (ii==-1)
                ERR1("Error: Missing Dens",nn);
              comp[c1].xd=cc>>4;
              comp[c1].yd=cc&0xF;
              MSG3("  Xd,Yd: %u,%u",nn-1,
                (N2)comp[c1].xd,(N2)comp[c1].yd);
              cc=comp[c1].xd; if (cc!=1 && cc!=2 && cc!=4)
                ERR1("Error: Invalid XDens",nn-1);
              cc=comp[c1].yd; if (cc!=1 && cc!=2 && cc!=4)
                ERR1("Error: Invalid YDens",nn-1);
              // QID/8
              GETC; if (ii==-1)
                ERR1("Error: Missing QID",nn);
              comp[c1].qid=cc;
              MSG2("  QID: %u",nn-1,(N2)comp[c1].qid);
              cc=comp[c1].qid; if (cc>=QMAX)
                ERR1("Error: Invalid QID",nn-1);
            } ll=0;
            break;
          case 0xDA:
            MSG1("Marker: SOS",nn-4);
            // Comp Num (1)
            if (ll<1)
              ERR1("Error: Missing Comp Num: Len",nn);
            GETC; if (ii==-1)
              ERR1("Error: Missing Comp Num: Eof",nn);
            sos.comps=cc;
            MSG2("  CompNum: %u",nn-1,(N2)sos.comps);
            if (sos.comps!=sof.comps)
              ERR1("Error: Invalid Comp Num",nn-1);
            ll-=1;
            // Components [C/1,Hdc/4,Hac/4]
            if (ll<2*(N2)sos.comps)
              ERR2("Error: Invalid length: %lu",nn,ll);
            for (c1=0;c1<sos.comps;++c1)
            { // CID/8
              GETC; if (ii==-1)
                ERR1("Error: Missing CID",nn);
              comp2[c1].cid=cc;
              MSG2("  CID: %u",nn-1,(N2)comp2[c1].cid);
              if (comp2[c1].cid!=comp[c1].cid)
                ERR1("Error: Wrong CID",nn-1);
              // HDC/4,HAC/4
              GETC; if (ii==-1)
                ERR1("Error: Missing HID",nn);
              comp2[c1].hdc=cc>>4;
              comp2[c1].hac=cc&0xF;
              MSG3("  hdc,hac: %u,%u",nn-1,
                (N2)comp2[c1].hdc,(N2)comp2[c1].hac);
              if (comp2[c1].hdc>=HNUM)
                ERR1("Error: Invalid HDC",nn-1);
              if (comp2[c1].hac>=HNUM)
                ERR1("Error: Invalid HAC",nn-1);
            } ll-=2*(N2)sos.comps;
            if (ll!=3)
              ERR1("Error: Invalid Length",nn);
            // Start Qt (1)
            GETC; if (ii==-1)
              ERR1("Error: Missing Start Qt",nn);
            sos.q1=cc;
            MSG2("  Start Qt: %u",nn-1,(N2)sos.q1);
            if (sos.q1!=0)
              ERR1("Error: Invalid Start Qt",nn-1);
            // Stop Qt (1)
            GETC; if (ii==-1)
              ERR1("Error: Missing Stop Qt",nn);
            sos.q2=cc;
            MSG2("  Stop Qt: %u",nn-1,(N2)sos.q2);
            if (sos.q2!=63)
              ERR1("Error: Invalid Stop Qt",nn-1);
            // Approx H/4,L/4
            GETC; if (ii==-1)
              ERR1("Error: Missing Approx",nn);
            sos.ah=cc>>4; sos.al=cc&0xF;
            MSG3("  Approx H,L: %u,%u",nn-1,
              (N2)sos.ah,(N2)sos.al);
            if (sos.ah || sos.al)
              ERR1("Error: Invalid Approx",nn-1);
            ll-=3;
            break;
          case 0xDB:
            MSG1("Marker: DQT",nn-4); do {
            if (qn>=QMAX)
              ERR1("Error: More DQT",nn-4);
            // Precision,QID (1)
            if (ll<1)
              ERR1("Error: Missing Prec/Qid: Len",nn);
            GETC; if (ii==-1)
              ERR1("Error: Missing Prec/Qid: Eof",nn);
            qtab[qn].prec=cc>>4; qtab[qn].qid=cc&0xF;
            MSG3("  Prec: %u (%u)",nn-1,
              8u<<qtab[qn].prec,(N2)qtab[qn].prec);
            MSG2("  Qid: %u",nn-1,(N2)qtab[qn].qid);
            if (qtab[qn].prec!=0)
              ERR1("Error: Invalid Prec",nn-1);
            if (qtab[qn].qid>=QMAX)
              ERR1("Error: Invalid Qid",nn-1);
            ll-=1;
            // Qtab (64)
            if (ll<(64<<qtab[qn].prec))
              ERR2("Error: Missing Qtab (%u)",nn,(N2)qn);
            for (c1=0;c1<64;++c1)
            { if ((c1&0x7)==0)
                INF1("[%08lX] ",nn);
              GETC; if (ii==-1)
                ERR1("Error: Missing Quant",nn);
              if (!cc)
                ERR2("Error: Zero Quant(%u)",nn-1,(N2)c1);
              qtab[qn].tab[c1]=(N2)cc;
              INF1("  %2u",(N2)cc);
              if ((c1&0x7)==7)
                INF0("\n");
            } ll-=64<<qtab[qn].prec;
            ++qn; } while (ll);
            break;
          case 0xC4:
            MSG1("Marker: DHT",nn-4); do {
            if (hn>=HMAX)
              ERR1("Error: More DHT",nn-4);
            if (ll<17)
              ERR2("Error: Missing Htab (%u)",nn,(N2)hn);
            // Type,Hid (1)
            GETC; if (ii==-1)
              ERR1("Error: Missing Typ/Hid",nn);
            htab[hn].typ=cc>>4; htab[hn].hid=cc&0xF;
            if (htab[hn].typ>1)
              ERR2("Error: Invalid Typ: %u",nn-1,
              (N2)htab[hn].typ);
            if (htab[hn].hid>=HNUM)
              ERR2("Error: Invalid Hid: %u",nn-1,
              (N2)htab[hn].hid);
            MSG3("  Typ: %s (%u)",nn-1,
              htyp[htab[hn].typ],(N2)htab[hn].typ);
            MSG2("  Hid: %u",nn-1,(N2)htab[hn].hid);
            MSG1("  Bits/Vals:",nn-1);
            // Length (16)
            INF1("[%08lX] ",nn);
            for (n2=0,c1=0;c1<16;++c1)
            { GETC; if (ii==-1)
                ERR2("Error: Missing Len(%u)",nn,(N2)c1);
              htab[hn].len[c1]=cc; n2+=(N4)cc;
              INF1("  %u",(N2)cc);
            } ll-=17;
            INF0("\n");
            htab[hn].hufnum=n2;
            // Values (n2)
            htab[hn].val=MemGet(n2);
            if (!htab[hn].val)
              ERR2("Error: Nomem: Val[%lu]",nn,n2);
            if (ll<n2)
              ERR2("Error: Missing Val[%lu]",nn,n2);
            MSG2("  (ValLen): %lu",nn,n2);
            for (n1=0;n1<n2;++n1)
            { if ((n1&0xF)==0)
                INF1("[%08lX] ",nn);
              GETC; if (ii==-1)
                ERR2("Error: Missing Val(%lu)",nn,n1);
              htab[hn].val[n1]=cc;
              INF1("  %u",(N2)cc);
              if ((n1&0xF)==0xF)
                INF0("\n");
            } ll-=n2;
            if (n1&0xF) INF0("\n");
            // hufsiz,hufcod
            htab[hn].hufsiz=MemGet(htab[hn].hufnum*1);
            htab[hn].hufcod=MemGet(htab[hn].hufnum*2);
            if (!htab[hn].hufsiz)
              ERR2("Error: Nomem: HufSiz[%u]",nn,htab[hn].hufnum);
            if (!htab[hn].hufcod)
              ERR2("Error: Nomem: HufCod[%u]",nn,htab[hn].hufnum);
            for (n1=n2=0,c1=0;c1<16;++c1,n2<<=1)
            { htab[hn].hufidx[c1]=n1;
              htab[hn].hufmin[c1]=(N2)n2;
              for (cc=0;cc<htab[hn].len[c1];++cc,++n1,++n2)
              { htab[hn].hufsiz[n1]=c1+1;
                htab[hn].hufcod[n1]=(N2)n2;
              }
              htab[hn].hufmax[c1]=(N2)n2-1;
              if (((I2)htab[hn].hufmax[c1])<
                  ((I2)htab[hn].hufmin[c1]))
                htab[hn].hufmin[c1]=
                htab[hn].hufmax[c1]=(N2)-1l;
            }
            MSG2("  (Codes): %lu",nn,n1);
            if (n1!=htab[hn].hufnum)
              ERR1("Error: HufNum",nn);
            // hufsiz,hufcod
            for (n1=0;n1<htab[hn].hufnum;++n1)
            { if ((n1&0x7)==0)
                INF1("[%08lX] ",nn);
              INF2("  %u:%04X",
                (N2)htab[hn].hufsiz[n1],
                htab[hn].hufcod[n1]);
              if ((n1&0x7)==0x7)
                INF0("\n");
            }
            if (n1&0x7) INF0("\n");
            ++hn; } while (ll);
            break;
          case 0xFE:
            WARN1("Warn: Marker: COM",nn-4);
            break;
          case 0xDD:
            MSG1("Marker: DRI",nn-4);
            if (ll!=2)
              ERR1("Error: Invalid Length",nn);
            // Restart Interval (2)
            GETC; if (ii==-1)
              ERR1("Error: Missing Restart Interval/1",nn);
            sos.dri=(N2)cc;
            GETC; if (ii==-1)
              ERR1("Error: Missing Restart Interval/2",nn-1);
            sos.dri=(sos.dri<<8)|(N2)cc;
            MSG2("  RestIval : %u",nn-2,sos.dri);
            ll-=2;
            break;
          default:
            WARN1("Warn: Unknown Marker",nn-4);
            break;
        }
    }
  }
  if (ll) ERR1("Error: Unexpected EOF",nn);
  if (prg.fi!=stdin) fclose(prg.fi);
  if (prg.fo!=stdout) fclose(prg.fo);
  if (prg.fb) fclose(prg.fb);

  return (0);
}

I4 conv1(N4 *nn,tSof *sof,tComp *comp,tComp2 *comp2,
  tHtab *htab,tQtab *qtab,tNextBit *bb,FILE *f1)
{ N1 c1; N4 n1,n2;
  N1 zz2[64],zz1[64]=
  { 0, 1, 5, 6,14,15,27,28,
    2, 4, 7,13,16,26,29,42,
    3, 8,12,17,25,30,41,43,
    9,11,18,24,31,40,44,53,
   10,19,23,32,39,45,52,54,
   20,22,33,38,46,51,55,60,
   21,34,37,47,50,56,59,61,
   35,36,48,49,57,58,62,63,
  }; R4 tt[64]; tBmp bmp;
  N1 *pp[CNUM],dd2[64];
  I2 dd[64],dd0[CNUM];
  R4 rr1[64],rr2[64];
  N2 xd,yd,xm,ym;
  N2 i1,i2,i3,i4;

  // ZigZag 1,2
  { I1 x1=0,y1=0,j1=0,up=1;
    for (;j1<64;++j1)
    { zz1[y1*8+x1]=j1;
      if (up) { ++x1; --y1; }
      else { --x1; ++y1; }
      if (up)
      { if (x1>7) { x1=7; y1+=2; up=0; }
        if (y1<0) { y1=0; up=0; }
      } else
      { if (y1>7) { y1=7; x1+=2; up=1; }
        if (x1<0) { x1=0; up=1; }
      }
    }
    for (x1=0;x1<64;++x1)
      zz2[zz1[x1]]=x1;
    DEB0("[........] (ZigZag):\n");
    for (j1=y1=0;y1<8;++y1)
    { DEB0("[........] [");
      for (x1=0;x1<8;++x1,++j1)
        DEB1(" %02i",zz1[j1]);
      DEB0("]    [");
      for (j1-=8,x1=0;x1<8;++x1,++j1)
        DEB1(" %02i",zz2[j1]);
      DEB0("]\n");
    }
  }
  // DCT const
  { R4 j1,j2; N2 i0=0;
    R4 r1=1.0/sqrt(2.0);
    for (j1=0.5;j1<8;++j1)
    { tt[i0++]=0.5*r1;
      for (j2=1.0;j2<8;++j2)
        tt[i0++]=0.5*(R4)cos
        (M_PI/8.0*j1*j2);
    }
    DEB0("[........] (DCT):\n");
    for (i0=0;i0<64;++i0)
    { if ((i0&7)==0)
        DEB0("[........] [");
      DEB1("  %f",tt[i0]);
      if ((i0&7)==7)
        DEB0("]\n");
    }
  }

  // Initalizing
  xd=yd=0;
  for (n1=0;n1<CNUM;++n1) dd0[n1]=0;
  for (n1=0;n1<CNUM;++n1)
  { if (comp[n1].xd>xd) xd=comp[n1].xd;
    if (comp[n1].yd>yd) yd=comp[n1].yd;
  }
  i1=comp[0].xd*comp[0].yd;
  i2=comp[1].xd*comp[1].yd+i1;
  i3=comp[2].xd*comp[2].yd+i2;
  xd*=8; xm=(sof->xl+xd-1)/xd*xd; xd/=8;
  yd*=8; ym=(sof->yl+yd-1)/yd*yd; yd/=8;
  for (n2=0,n1=0;n1<CNUM;++n1)
    n2+=(comp[n1].xd*(N4)xm/xd/8)*
        (comp[n1].yd*(N4)ym/yd/8);
  MSG4("Image (%u,%u) (%lu)",*nn-1,xm,ym,n2);
  for (i4=xd*yd*64,n1=0;n1<CNUM;++n1)
    if (!(pp[n1]=MemGet(i4)))
      ERR3("Error: Image: Nomem: PP%u[%u]",*nn-1,n1+1,i4);
  for (i4=0;i4<50;i4+=i3); if (i4>50) i4-=i3;
  if (BmpBeg(&bmp,f1,xm,ym,sof->xl,sof->yl)==-1l)
    ERR1("Error: Image: BmpBeg",*nn-1);

  // DataUnit (8x8)
  for (n1=0;n1<n2;++n1)
  { I4 ii; N1 cid=n1%i3;
    tHtab *h1; tQtab *q1;
    cid=(cid<i1)?0:(cid<i2)?1:2;
    if (n1%i4==0)
      DEB2("[%08lX+%u] ",
      *nn-1,8-bb->cnt);
    if (n1%i3==0)
      DEB0("<");
    // !! Decode !!
    h1=getht(htab,comp2[cid].hdc,0);
    if (!h1) ERR3("Error: Image: Htab %u(%u,0)",
      *nn-1,(N2)cid,(N2)comp2[cid].hdc);
    dd[0]=dd0[cid];
    ii=GetDC(bb,h1,dd+0);
    dd0[cid]=dd[0];
    if (ii==-1l) return (-1l);
    h1=getht(htab,comp2[cid].hac,1);
    if (!h1) ERR3("Error: Image: Htab %u(%u,1)",
      *nn-1,(N2)cid,(N2)comp2[cid].hac);
    ii=GetAC(bb,h1,dd+1);
    if (ii==-1l) return (-1l);
    DEB0(".");
    if (n1==0) dumpI2("Decode",dd);
    // !! Quant2,ZigZag2 !!
    q1=getqt(qtab,comp[cid].qid);
    if (!q1) ERR3("Error: Image: Qtab %u(%u)",
      *nn-1,(N2)cid,(N2)comp[cid].qid);
    if (Quant2(q1,dd,rr1)==-1l) return (-1l);
    if (n1==0) dumpR4("Quant2",rr1);
    if (ZigZag(zz2,rr1,rr2)==-1l) return (-1l);
    if (n1==0) dumpR4("ZigZag2",rr2);
    // !! DCT2,LevelShift2 !!
    if (DCT2(tt,rr2,rr1)==-1l) return (-1l);
    if (n1==0) dumpR4("DCT2",rr1);
    if (LevelShift2(rr1,dd2)==-1l) return (-1l);
    if (n1==0) dumpN1("LevelShift2",dd2);
    // !! DU store !!
    { I2 x1,y1; c1=n1%i3;
      c1-=(c1<i1)?0:(c1<i2)?i1:i2;
      y1=c1/comp[cid].xd;
      x1=c1%comp[cid].xd;
      ddcp(dd2,ddyx(pp[cid],xd,y1,x1));
      // !! MCU fill !!
      if (n1%i3==i3-1)
      { for (c1=0;c1<CNUM;++c1)
        { Str s1,s2; N2 x2,y2;
          N2 xl=comp[c1].xd;
          N2 yl=comp[c1].yd;
          N2 x1a,x1b,x2a,x2b,kk;
          N2 y1a,y1b,y2a,y2b,xd2=xd*64;
          y2=yl*8; y1a=0; y1b=0;
          s1=pp[c1]; s2=pp[c1];
          if (xl!=xd)
            for (y1=0;y1<y2;++y1)
          { x1=xd*8-1; x1a=x1/8; x1b=x1%8;
            x2=x1*xl/xd; x2a=x2/8; x2b=x2%8;
            s1+=x1a*64+x1b; s2+=x2a*64+x2b;
            for (kk=x1*xl%xd;x1>=0;--x1)
            { //s1=ddyx(pp[c1],xd,y1/8,x1/8);
              //s2=ddyx(pp[c1],xd,y1/8,x2/8);
              //s1[y1%8*8+x1%8]=s2[y1%8*8+x2%8];
              *s1=*s2; --s1;
              if (--x1b==0xFFFF)
              { --x1a; x1b=7; s1-=56; }
              if ((kk-=xl)>=0x8000)
              { kk+=xd; --x2; --s2;
                if (--x2b==0xFFFF)
                { --x2a; x2b=7; s2-=56; }
              }
            } s1+=57; s2+=57;
            if (++y1b==8)
            { ++y1a; y1b=0;
              s1+=xd2-7*8;
              s2+=xd2-7*8;
            } else
            { s1+=8; s2+=8; }
          }
          y1=yd*8-1; y1a=y1/8; y1b=y1%8;
          y2=y1*yl/yd; y2a=y2/8; y2b=y2%8;
          s1=pp[c1]+y1a*xd2+y1b*8;
          s2=pp[c1]+y2a*xd2+y2b*8;
          if (yl!=yd)
            for (kk=y1*yl%yd;y1>=0;--y1)
          { Str t1=s1,t2=s2;
            x2=xd*8; x1a=0; x1b=0;
            for (x1=0;x1<x2;++x1)
            { //s1=ddyx(pp[c1],xd,y1/8,x1/8);
              //s2=ddyx(pp[c1],xd,y2/8,x1/8);
              //s1[y1%8*8+x1%8]=s2[y2%8*8+x1%8];
              *s1=*s2; ++s1; ++s2;
              if (++x1b==8)
              { ++x1a; x1b=0; s1+=56; s2+=56; }
            } s1=t1; s2=t2;
            if (--y1b!=0xFFFF) s1-=8; else
            { --y1a; y1b=7; s1-=xd2-7*8; }
            if ((kk-=yl)>=0x8000)
            { kk+=yd; --y2;
              if (--y2b!=0xFFFF) s2-=8; else
              { --y2a; y2b=7; s2-=xd2-7*8; }
            }
          }
        }
      }
    }
    // !! Display !!
    if (n1%i3==i3-1)
    { Str s1,s2,s3;
      N2 x1,y1,x,y;
      N4 n3=n1/i3;
      N2 y2=(N2)(n3/(xm/xd/8));
      N2 x2=(N2)(n3%(xm/xd/8));
      N2 i1x,i1y,i2x,i2y;
      x2*=xd*8; y2*=yd*8;
      s1=pp[0]; s2=pp[1]; s3=pp[2];
      for (i1y=y2,y1=0;y1<yd;++y1,i1y+=8)
      { for (i1x=x2,x1=0;x1<xd;++x1,i1x+=8)
        { //s1=ddyx(pp[0],xd,y1,x1);
          //s2=ddyx(pp[1],xd,y1,x1);
          //s3=ddyx(pp[2],xd,y1,x1);
          for (i2y=i1y,y=0;y<8;++y,++i2y)
          { for (i2x=i1x,x=0;x<8;++x,++i2x)
            { Color2(s1,s2,s3);
              BmpSet(&bmp,i2x,i2y,
                *(s1++),*(s2++),*(s3++));
            }
          }
        }
      }
    }
    if (n1%i3==i3-1)
      DEB0(">");
    if (n1%i4==i4-1)
      DEB0("\n");
  }
  if (n1%i4) DEB0("\n");

  // Finishing
  while (bb->cnt)
  { I4 ii=NextBit(bb);
    if (ii==-1l) break;
    if (ii) continue;
    WARN1("Warn: Image: HufEnd",*nn-1);
  }
  if (BmpEnd(&bmp)==-1l)
    ERR1("Error: Image: BmpEnd",*nn);
  return (0);
}

I4 NextBit(tNextBit *r1)
{ if (!r1)
    ERR1("Error: NextBit: Param",-1l);
  // NextByte
  while (!r1->cnt)
  { int ii=fgetc(r1->f1);
    if (ii==-1)
      ERR1("Error: NextBit: EOF/1",*r1->nn);
    r1->bb=(N1)ii; r1->cnt=8; ++*r1->nn;
    // Escape
    if (r1->bb==0xFF)
    { ii=fgetc(r1->f1);
      if (ii==-1)
        ERR1("Error: NextBit: EOF/2",*r1->nn);
      ++*r1->nn;
      switch (ii)
      { case 0:
          break;
        case 0xD0:
        case 0xD1:
        case 0xD2:
        case 0xD3:
        case 0xD4:
        case 0xD5:
        case 0xD6:
        case 0xD7:
          r1->bb=0; r1->cnt=0;
          DEB1("R%c",ii-0xD0+'0');
          break;
        default:
          ERR2("Error: NextBit: Marker: 0x%02X",
          *r1->nn-2,(N2)(N1)ii);
      }
    }
  }
  // NextBit
  { N1 bb=r1->bb>>7;
    r1->bb<<=1; --r1->cnt;
    return ((I4)(N4)bb);
  }
}

I4 NextVal(tNextBit *r1,N1 bits)
{ N2 n1,n2=0;

  if (bits>16)
    ERR1("Error: NextVal: Param",-1l);
  for (n1=0;n1<bits;++n1)
  { I4 ii=NextBit(r1);
    if (ii==-1l) return (-1l);
    n2=(n2<<1)|(N2)ii;
  }
  // Extend (sign)
  if (bits)
  { n1=1<<(bits-1);
    if (n2<n1)
    { n1=-1<<bits;
      n2+=n1+1;
    }
  }

  return ((I4)(N4)n2);
}

I4 Decode(tNextBit *r1,tHtab *htab)
{ N2 n1,n2=0;

  if (!htab)
    ERR1("Error: Decode: Param",-1l);
  for (n1=0;n1<16;++n1)
  { I4 ii=NextBit(r1);
    if (ii==-1l) return (-1l);
    n2=(n2<<1)|(N2)ii;
    if (htab->hufmax[n1]==(N2)-1l) continue;
    if (n2<=htab->hufmax[n1]) break;
  }
  if (n1==16)
    ERR1("Error: Decode",*r1->nn);
  n2=htab->hufidx[n1]+n2-htab->hufmin[n1];
  return ((I4)(N4)htab->val[n2]);
}

I4 GetDC(tNextBit *r1,tHtab *htab,I2 *dc)
{ I4 ii;

  if (!dc)
    ERR1("Error: GetDC: Param",-1l);
  ii=Decode(r1,htab);
  if (ii==-1l) return (-1l);
  ii=NextVal(r1,(N1)ii);
  if (ii==-1l) return (-1l);
  *dc+=(I2)ii; return (0);
}

I4 GetAC(tNextBit *r1,tHtab *htab,I2 *ac)
{ N2 n1,n2;

  if (!ac)
    ERR1("Error: GetAC: Param",-1l);
  for (n1=0;n1<63;++n1) ac[n1]=0;
  for (n1=0;n1<63;++n1)
  { I4 ii=Decode(r1,htab);
    if (ii==-1l) return (-1l);
    if (!ii) break;
    if (ii==0xF0)
    { if (n1+16>63)
        ERR2("Error: GetAC[%u]: 0xF0(16)",
        *r1->nn,n1);
      n1+=15; continue;
    }
    n2=((N2)ii)>>4;
    if (n1+n2>63)
      ERR3("Error: GetAC[%u]: RR(%u)",
      *r1->nn,n1,n2);
    n1+=n2; n2=((N2)ii)&0xF;
    if (!n2)
      ERR2("Error: GetAC[%u]: SS(0)",
      *r1->nn,n1);
    ii=NextVal(r1,(N1)n2);
    if (ii==-1l) return (-1l);
    if (n1==63)
      ERR3("Error: GetAC[%u]: SS(%u)",
      *r1->nn,n1,n2);
    ac[n1]=(I2)ii;
  }

  return (0);
}

I4 ZigZag(N1 *zz,R4 *dd1,R4 *dd2)
{ N2 i1; for (i1=0;i1<64;++i1)
    dd2[*(zz++)]=*(dd1++);
  return (0);
}

I4 LevelShift1(N1 *dd1,R4 *dd2)
{ I2 i2; N2 i1;
  for (i1=0;i1<64;++i1)
  { i2=((I2)(N2)*(dd1++))-128;
    *(dd2++)=(R4)i2;
  }
  return (0);
}
I4 LevelShift2(R4 *dd1,N1 *dd2)
{ R4 rr; N2 i1;
  for (i1=0;i1<64;++i1)
  { rr=RoundR4(*(dd1++))+128;
    if (rr<0) rr=0; else
    if (rr>255) rr=255;
    *(dd2++)=(N1)rr;
  }
  return (0);
}

I4 Quant1(tQtab *qtab,R4 *dd1,I2 *dd2)
{ N2 i1,*q1=qtab->tab;
  for (i1=0;i1<64;++i1)
    *(dd2++)=(I2)RoundR4(*(dd1++)/((R4)*(q1++)));
  return (0);
}
I4 Quant2(tQtab *qtab,I2 *dd1,R4 *dd2)
{ N2 i1,*q1=qtab->tab;
  for (i1=0;i1<64;++i1)
    *(dd2++)=((R4)*(dd1++))*((R4)*(q1++));
  return (0);
}

I4 DCT1(R4 *tt,R4 *dd1,R4 *dd2)
{ N2 u,v;
  for (v=0;v<8;++v)
    for (u=0;u<8;++u)
      *(dd2++)=DCT1x(tt,dd1,u,v);
  return (0);
}
I4 DCT2(R4 *tt,R4 *dd1,R4 *dd2)
{ N2 x,y;
  for (y=0;y<8;++y)
    for (x=0;x<8;++x)
      *(dd2++)=DCT2x(tt,dd1,x,y);
  return (0);
}
R4 DCT1x(R4 *tt,R4 *dd,N1 u,N1 v)
{ N2 x,y; R4 rr=0.0;
  R4 *t0,*t1=tt+u,*t2=tt+v;
  //for (y=0;y<8;++y)
  //  for (x=0;x<8;++x)
  //    rr+=*(dd++)*tt[x*8+u]*tt[y*8+v];
  for (t0=t1,y=0;y<8;++y,t2+=8)
    for (t1=t0,x=0;x<8;++x,t1+=8)
      rr+=*(dd++)*(*t1)*(*t2);
  return (rr);
}
R4 DCT2x(R4 *tt,R4 *dd,N1 x,N1 y)
{ N2 u,v; R4 rr=0.0;
  R4 *t0,*t1=tt+x*8,*t2=tt+y*8;
  //for (v=0;v<8;++v)
  //  for (u=0;u<8;++u)
  //    rr+=*(dd++)*tt[x*8+u]*tt[y*8+v];
  for (t0=t1,v=0;v<8;++v,++t2)
    for (t1=t0,u=0;u<8;++u,++t1)
      rr+=*(dd++)*(*t1)*(*t2);
  return (rr);
}

xx Color1(N1 *cr,N1 *cg,N1 *cb)
{ R4 c1=RoundR4(0.299*(*cr)+0.587*(*cg)+0.114*(*cb));
  R4 c2=RoundR4(-0.1687*(*cr)-0.3313*(*cg)+0.5*(*cb)+128);
  R4 c3=RoundR4(0.5*(*cr)-0.4187*(*cg)-0.0813*(*cb)+128);
  if (c1<0) c1=0; else
  if (c1>255) c1=255;
  if (c2<0) c2=0; else
  if (c2>255) c2=255;
  if (c3<0) c3=0; else
  if (c3>255) c3=255;
  *cr=(N1)c1; *cg=(N1)c2; *cb=(N1)c3;
}
xx Color2(N1 *c1,N1 *c2,N1 *c3)
{ R4 r1=(R4)(N2)(*c1);
  R4 r2=(R4)(N2)(*c2)-128;
  R4 r3=(R4)(N2)(*c3)-128;
  R4 cr=RoundR4(r1+1.402*r3);
  R4 cg=RoundR4(r1-0.34414*r2-0.71414*r3);
  R4 cb=RoundR4(r1+1.772*r2);
  if (cr<0) cr=0; else
  if (cr>255) cr=255;
  if (cg<0) cg=0; else
  if (cg>255) cg=255;
  if (cb<0) cb=0; else
  if (cb>255) cb=255;
  *c1=(N1)cr; *c2=(N1)cg; *c3=(N1)cb;
}

I4 BmpBeg(tBmp *bmp,FILE *f1,
  N4 xm,N4 ym,N4 xl,N4 yl)
{ N4 n; Str s;
  if (!bmp)
    ERR1("Error: BmpBeg: Param",0ul);
  bmp->f1=f1; bmp->pp=NULL;
  if (!f1) return (0);
  if (xm<xl) xm=xl;
  if (ym<yl) ym=yl;
  bmp->xm=xm; bmp->ym=ym;
  bmp->xl=xl; bmp->yl=yl;
  bmp->xm2=(xm*3+3)/4*4;
  bmp->xl2=(xl*3+3)/4*4;
  if (!(s=bmp->pp=
    MemGet(n=ym*bmp->xm2)))
    ERR1("Error: BmpBeg: Nomem",0ul);
  while (n--) *(s++)=0;

#if !defined(___SYS_I386)
#error Invalid system
#endif
  // Init (file)
  fputc('B',f1);
  fputc('M',f1);
  // ..4:FileLen
  n=14+40+yl*bmp->xl2;
  fwrite(&n,4,1,f1);
  // ..4:AppData
  n=0;
  fwrite(&n,4,1,f1);
  // ..4:BmpOffs
  n=14+40;
  fwrite(&n,4,1,f1);
  // Init (bmp)
  // ..4:InfoLen
  n=40;
  fwrite(&n,4,1,f1);
  // ..4:XLen
  n=xl;
  fwrite(&n,4,1,f1);
  // ..4:YLen
  n=yl;
  fwrite(&n,4,1,f1);
  // ..2:Type(disp,prn,...)
  n=1;
  fwrite(&n,2,1,f1);
  // ..2:Depth
  n=24;
  fwrite(&n,2,1,f1);
  // ..4:Compression
  n=0;
  fwrite(&n,4,1,f1);
  // ..4:BmpLen
  n=0;
  fwrite(&n,4,1,f1);
  // ..4:XDens (dpm)
  n=0;
  fwrite(&n,4,1,f1);
  // ..4:YDens (dpm)
  n=0;
  fwrite(&n,4,1,f1);
  // ..4:PalNum
  n=0;
  fwrite(&n,4,1,f1);
  // ..4:PalUsed
  n=0;
  fwrite(&n,4,1,f1);

  fflush(f1);
  return (0);
}
I4 BmpEnd(tBmp *bmp)
{ N4 i1,i2; Str s1;
  if (!bmp || !bmp->pp)
    ERR1("Error: BmpEnd: Param",0ul);
  if (!bmp->f1) return (0);
  s1=bmp->pp;
  for (i1=0;i1<bmp->yl;++i1)
  { i2=bmp->xl*3; s1+=i2;
    for (;i2<bmp->xl2;++i2)
      *(s1++)=0;
    s1+=bmp->xm2-bmp->xl2;
  }
  s1=bmp->pp+bmp->yl*bmp->xm2;
  for (i1=0;i1<bmp->yl;++i1)
  { s1-=bmp->xm2;
    fwrite(s1,bmp->xl2,1,bmp->f1);
  }
  MemRel(bmp->pp);
  bmp->pp=NULL;
  return (0);
}
I4 BmpSet(tBmp *bmp,N4 x,N4 y,
  N1 cr,N1 cg,N1 cb)
{ Str s;
  if (!bmp) return (-1l);
  if (!bmp->f1) return (0);
  if (!bmp->pp) return (-1l);
  s=bmp->pp+y*bmp->xm2+x*3;
  *(s++)=cb; *(s++)=cg; *(s++)=cr;
  return (0);
}
